#include "RrcAsnCodecApi.h"
#include "AL_RRC_BCCH_DL_SCH_Message.h"

#include <arpa/inet.h>

class RrcAsnCodecCleaner
{
public:
    RrcAsnCodecCleaner(AL_RRC_BCCH_DL_SCH_Message* dlschMsg, AsnBuffer* decodeBuf):
    msg_(dlschMsg),
    buf_(decodeBuf)
    {
    };

    ~RrcAsnCodecCleaner()
    {
        if( msg_!=NULL )
        {
            free_BCCH_DL_SCH_Message(msg_);
        }

        if( buf_!=NULL )
        {
            asn_free(buf_);
        }
    }
private:
    AL_RRC_BCCH_DL_SCH_Message* msg_;
    AsnBuffer* buf_;
};

bool RrcAsnCodecApi::decodeBcchDlSchMessageType(const uint8_t* buffer,
                                                const uint32_t length,
                                                RrcMessageType& msgType)
{    
    AL_RRC_BCCH_DL_SCH_Message dlschMsg;
    AsnBuffer* decodeBuffer = wrap_buffer(reinterpret_cast<char*>(const_cast<uint8_t*>(buffer)), length, UNALIGNED_PACKED_ENCODING_RULES);
    if (decodeBuffer == NULL)
    {
        return false;
    }

    memset(&dlschMsg, 0, sizeof(dlschMsg));
    int result = decode_BCCH_DL_SCH_Message(decodeBuffer, &dlschMsg);
    RrcAsnCodecCleaner cleaner(&dlschMsg, decodeBuffer);
    
    if (0 != result)
    {
        return false;
    }

    if( AL_RRC_BCCH_DL_SCH_MessageType_c1_chosen==dlschMsg.message.choice )
    {
        if( AL_RRC_BCCH_DL_SCH_MessageType_C1_systemInformationBlockType1_chosen== dlschMsg.message.value.c1.choice )
        {
            msgType = SYSTEM_INFORMATION_BLOCK_TYPE1;
        }
        else if( AL_RRC_BCCH_DL_SCH_MessageType_C1_systemInformation_chosen== dlschMsg.message.value.c1.choice )
        {
            msgType = SYSTEM_INFORMATION_BLOCKS;
        }
    }
    return true;
}

bool RrcAsnCodecApi::decodeRrcSystemInformationBlockType1(const uint8_t* buffer,
                                                                  const uint32_t length,
                                                                  RrcSystemInformationBlockType1& rrcSib1)
{
    AL_RRC_BCCH_DL_SCH_Message dlschMsg;
    AsnBuffer* decodeBuffer = wrap_buffer(reinterpret_cast<char*>(const_cast<uint8_t*>(buffer)), length, UNALIGNED_PACKED_ENCODING_RULES);
    if (decodeBuffer == NULL)
    {
        return false;
    }

    memset(&dlschMsg, 0, sizeof(dlschMsg));
    int result = decode_BCCH_DL_SCH_Message(decodeBuffer, &dlschMsg);
    RrcAsnCodecCleaner cleaner(&dlschMsg, decodeBuffer);
    
    if (0 != result)
    {
        return false;
    }

    if( AL_RRC_BCCH_DL_SCH_MessageType_c1_chosen==dlschMsg.message.choice )
    {
        if( AL_RRC_BCCH_DL_SCH_MessageType_C1_systemInformationBlockType1_chosen== dlschMsg.message.value.c1.choice )
        {
            struct AL_RRC_SystemInformationBlockType1& sib1 = dlschMsg.message.value.c1.value.systemInformationBlockType1;

            if( RrcCellAccessRelatedInfo::BITS_TRACK_AREA_CODE!=sib1.cellAccessRelatedInfo.trackingAreaCode.length )
            {
                return false;
            }
            memcpy(&rrcSib1.cellAccessRelatedInfo_.trackingAreaCode_, sib1.cellAccessRelatedInfo.trackingAreaCode.bytes, sib1.cellAccessRelatedInfo.trackingAreaCode.length);
            rrcSib1.cellAccessRelatedInfo_.trackingAreaCode_ = ntohs(rrcSib1.cellAccessRelatedInfo_.trackingAreaCode_);

            uint32_t v32 = sib1.freqBandIndicator;
            rrcSib1.freqBandIndicator_ = static_cast<uint16_t>(v32);
            rrcSib1.siWindowLength_ = static_cast<RrcSiWindowLength>(sib1.si_WindowLength);

            v32 = sib1.systemInfoValueTag;
            rrcSib1.systemInfoValueTag_ = static_cast<uint16_t>(v32);

            if( sib1.schedulingInfoList.size > rrcSib1.schedulingInfoList_.value_.MAX_NUM ||
                sib1.schedulingInfoList.size < rrcSib1.schedulingInfoList_.value_.MIN_NUM )
            {
                return false;
            }

            rrcSib1.schedulingInfoList_.value_.numElem_= sib1.schedulingInfoList.size;
            for(uint32_t i=0; i<sib1.schedulingInfoList.size; i++)
            {
                if( !getValue(sib1.schedulingInfoList.elements[i], rrcSib1.schedulingInfoList_.value_.list_[i]) )
                {
                    return false;
                }
            }

            if( sib1.cellAccessRelatedInfo.plmn_IdentityList.size!=0 )
            {
                if( sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mcc!=NULL )
                {
                    rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mcc->elements[2]<<12;
                    rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mcc->elements[1]<<16;
                    rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mcc->elements[0]<<20;
                }

                rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mnc.elements[0] << 8;
                rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mnc.elements[1] << 4;
                if( sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mnc.size==3 )
                {
                    rrcSib1.plmnId_ |= sib1.cellAccessRelatedInfo.plmn_IdentityList.elements[0].plmn_Identity.mnc.elements[2];
                }
                else
                {
                    rrcSib1.plmnId_ |= 0xF;
                }
            }

            if( sib1.tdd_Config!=NULL )
            {
                if( !getValue(sib1.tdd_Config->subframeAssignment, rrcSib1.tddConfig_.subFrameAssign_) )
                {
                    return false;
                }

                if( !getValue(sib1.tdd_Config->specialSubframePatterns, rrcSib1.tddConfig_.specSubFramePattern_) )
                {
                    return false;
                }

                
                rrcSib1.isTddConfigPresent_ = true;
            }
            return true;
        }
    }

    return true;
}

bool RrcAsnCodecApi::decodeRrcSystemInformation(const uint8_t* buffer,
                                     const uint32_t length,
                                     RrcSystemInformation& applMsg)
{
    AL_RRC_BCCH_DL_SCH_Message dlschMsg;
    AsnBuffer* decodeBuffer = wrap_buffer(reinterpret_cast<char*>(const_cast<uint8_t*>(buffer)), length, UNALIGNED_PACKED_ENCODING_RULES);
    if (decodeBuffer == NULL)
    {
        return false;
    }

    memset(&dlschMsg, 0, sizeof(dlschMsg));
    int result = decode_BCCH_DL_SCH_Message(decodeBuffer, &dlschMsg);
    if (0 != result)
    {
        free_BCCH_DL_SCH_Message(&dlschMsg);
        asn_free(decodeBuffer);
        return false;
    }

    if( AL_RRC_BCCH_DL_SCH_MessageType_c1_chosen==dlschMsg.message.choice )
    {
        if( AL_RRC_BCCH_DL_SCH_MessageType_C1_systemInformation_chosen== dlschMsg.message.value.c1.choice )
        {
            if( AL_RRC_SystemInformation_CriticalExtensions_systemInformation_r8_chosen==dlschMsg.message.value.c1.value.systemInformation.criticalExtensions.choice )
            {
                struct AL_RRC_SystemInformation_r8_IEs& sibs = dlschMsg.message.value.c1.value.systemInformation.criticalExtensions.value.systemInformation_r8;

                for(uint32_t i=0; i<sibs.sib_TypeAndInfo.size; i++)
                {
                    if( AL_RRC_Sib_TypeAndInfo_sib3_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                    {
                        //todo
                        //RrcSystemInformationBlockType3* inputSib3 = applMsg.getNewSibForWrite<RrcSystemInformationBlockType3>();
                    }
                    else if( AL_RRC_Sib_TypeAndInfo_sib4_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                    {
                        RrcSystemInformationBlockType4* inputSib4 = applMsg.getNewSibForWrite<RrcSystemInformationBlockType4>();
                        if( !getValue(sibs.sib_TypeAndInfo.elements[i].value.sib4, *inputSib4) )
                        {
                            free_BCCH_DL_SCH_Message(&dlschMsg);
                            asn_free(decodeBuffer);
                            return false;
                        }
                    }
                    else if( AL_RRC_Sib_TypeAndInfo_sib5_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                    {
                        RrcSystemInformationBlockType5* inputSib5 = applMsg.getNewSibForWrite<RrcSystemInformationBlockType5>();
                        if( !getValue(sibs.sib_TypeAndInfo.elements[i].value.sib5, *inputSib5) )
                        {
                            free_BCCH_DL_SCH_Message(&dlschMsg);
                            asn_free(decodeBuffer);
                            return false;
                        }
                    }
                    else
                    {
                        //ignore others
                        std::string rcvSibType = "unkown";
                        if( AL_RRC_Sib_TypeAndInfo_sib2_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib2";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib6_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib6";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib7_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib7";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib8_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib8";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib9_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib9";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib10_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib10";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib11_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib11";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib12_v920_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib12";
                        }
                        else if( AL_RRC_Sib_TypeAndInfo_sib13_v920_chosen==sibs.sib_TypeAndInfo.elements[i].choice )
                        {
                            rcvSibType = "sib13";
                        }
                    }
                }
            }
        }
    }

    free_BCCH_DL_SCH_Message(&dlschMsg);
    asn_free(decodeBuffer);
    return true;
}

bool RrcAsnCodecApi::getValue(struct AL_RRC_SystemInformationBlockType4& asnItem, RrcSystemInformationBlockType4& applItem)
{
    if( asnItem.intraFreqNeighCellList==NULL )
    {
        applItem.isIntraFreqNeighCellListPresent_ = false;
    }
    else
    {
        applItem.isIntraFreqNeighCellListPresent_ = true;

        if( asnItem.intraFreqNeighCellList->size==0 || asnItem.intraFreqNeighCellList->size>MAX_CELL_INTRA )
        {
            return false;
        }

        applItem.intraFreqNeighCellList_.numElem_ = asnItem.intraFreqNeighCellList->size;
        for(uint32_t i=0; i<asnItem.intraFreqNeighCellList->size; i++)
        {
            struct AL_RRC_IntraFreqNeighCellInfo& asnNeiCellInfo = asnItem.intraFreqNeighCellList->elements[i];
            uint32_t v32 = asnNeiCellInfo.physCellId;
            applItem.intraFreqNeighCellList_.list_[i].physCellId_ = static_cast<uint16_t>(v32);
        }
    }
    return true;
}

bool RrcAsnCodecApi::getValue(struct AL_RRC_SystemInformationBlockType5& asnItem, RrcSystemInformationBlockType5& applItem)
{
    if( asnItem.interFreqCarrierFreqList.size==0 || asnItem.interFreqCarrierFreqList.size>MAX_CARRIER_FREQ )
    {
        return false;
    }
    applItem.interFreqCarrierFreqList_.numElem_ = asnItem.interFreqCarrierFreqList.size;
    for(uint32_t i=0; i<asnItem.interFreqCarrierFreqList.size; i++)
    {
        struct AL_RRC_InterFreqCarrierFreqInfo& freqInfo = asnItem.interFreqCarrierFreqList.elements[i];
        uint32_t v32 = freqInfo.dl_CarrierFreq;
        applItem.interFreqCarrierFreqList_.list_[i].dlCarrierFreq_ = static_cast<uint16_t>(v32);
        if( freqInfo.interFreqNeighCellList!=NULL )
        {          
            if( freqInfo.interFreqNeighCellList->size==0 || freqInfo.interFreqNeighCellList->size>MAX_CELL_INTER )
            {
                return false;
            }
            applItem.interFreqCarrierFreqList_.list_[i].isInterFreqNeighCellListPresent_ = true;

            applItem.interFreqCarrierFreqList_.list_[i].interFreqNeighCellList_.numElem_ = freqInfo.interFreqNeighCellList->size;
            for(uint32_t j=0; j<freqInfo.interFreqNeighCellList->size; j++)
            {
                InterFreqNeighCellInfo& applNeighCell = applItem.interFreqCarrierFreqList_.list_[i].interFreqNeighCellList_.list_[j];
                applNeighCell.physCellId_ = freqInfo.interFreqNeighCellList->elements[j].physCellId;
            }
        }
        else
        {
            applItem.interFreqCarrierFreqList_.list_[i].isInterFreqNeighCellListPresent_ = false;
        }
    }
    return true;
}

bool RrcAsnCodecApi::getValue(AL_RRC_SchedulingInfo& asnItem, SchedulingInfo& rrcItem)
{
    switch(asnItem.si_Periodicity)
    {
        case AL_RRC_Si_Periodicity_rf8:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF8;
            break;
        }
        case AL_RRC_Si_Periodicity_rf16:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF16;
            break;
        }
        case AL_RRC_Si_Periodicity_rf32:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF32;
            break;
        }
        case AL_RRC_Si_Periodicity_rf64:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF64;
            break;
        }
        case AL_RRC_Si_Periodicity_rf128:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF128;
            break;
        }
        case AL_RRC_Si_Periodicity_rf256:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF256;
            break;
        }
        case AL_RRC_Si_Periodicity_rf512:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_RF512;
            break;
        }
        default:
        {
            rrcItem.siPeriodicity_ = PERIODICITY_SPARE;
            break;
        }
    }

    if( asnItem.sib_MappingInfo.size > rrcItem.sibMappingInfo_.value_.MAX_NUM ||
        asnItem.sib_MappingInfo.size < rrcItem.sibMappingInfo_.value_.MIN_NUM )
    {
        return false;
    }

    rrcItem.sibMappingInfo_.value_.numElem_ = asnItem.sib_MappingInfo.size;
    for(uint32_t i=0; i<asnItem.sib_MappingInfo.size; i++)
    {
        switch(asnItem.sib_MappingInfo.elements[i])
        {
            case AL_RRC_SIB_Type_sibType3:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE3;
                break;
            }
            case AL_RRC_SIB_Type_sibType4:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE4;
                break;
            }
            case AL_RRC_SIB_Type_sibType5:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE5;
                break;
            }
            case AL_RRC_SIB_Type_sibType6:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE6;
                break;
            }
            case AL_RRC_SIB_Type_sibType7:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE7;
                break;
            }
            case AL_RRC_SIB_Type_sibType8:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE8;
                break;
            }
            case AL_RRC_SIB_Type_sibType9:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE9;
                break;
            }
            case AL_RRC_SIB_Type_sibType10:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE10;
                break;
            }
            case AL_RRC_SIB_Type_sibType11:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE11;
                break;
            }
            case AL_RRC_SIB_Type_sibType12_v920:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE12;
                break;
            }
            case AL_RRC_SIB_Type_sibType13_v920:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = SIB_TYPE13;
                break;
            }
            default:
            {
                rrcItem.sibMappingInfo_.value_.list_[i] = NUM_OF_SIB_TYPE;
                return false;
            }
        }
    }
    return true;
}

bool RrcAsnCodecApi::getValue(AL_RRC_SubframeAssignment& ansItem, RrcSubFrameAssignment& applItem)
{
    switch(ansItem)
    {
        case AL_RRC_SubframeAssignment_sa0:
        {
            applItem = SUB_FRM_ASSGN_SA0;
            break;
        }
        case AL_RRC_SubframeAssignment_sa1:
        {
            applItem = SUB_FRM_ASSGN_SA1;
            break;
        }
        case AL_RRC_SubframeAssignment_sa2:
        {
            applItem = SUB_FRM_ASSGN_SA2;
            break;
        }
        case AL_RRC_SubframeAssignment_sa3:
        {
            applItem = SUB_FRM_ASSGN_SA3;
            break;
        }
        case AL_RRC_SubframeAssignment_sa4:
        {
            applItem = SUB_FRM_ASSGN_SA4;
            break;
        }
        case AL_RRC_SubframeAssignment_sa5:
        {
            applItem = SUB_FRM_ASSGN_SA5;
            break;
        }
        case AL_RRC_SubframeAssignment_sa6:
        {
            applItem = SUB_FRM_ASSGN_SA6;
            break;
        }
        default:
        {
            applItem = NUM_SUB_FRM_ASSGN;
            return false;
        }
    }
    return true;
}

bool RrcAsnCodecApi::getValue(AL_RRC_SpecialSubframePatterns& ansItem, RrcSpecSubFramePatterns& applItem)
{
    switch(ansItem)
    {
        case AL_RRC_SpecialSubframePatterns_ssp0:
        {
            applItem = FRM_PATTERN_SSP0;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp1:
        {
            applItem = FRM_PATTERN_SSP1;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp2:
        {
            applItem = FRM_PATTERN_SSP2;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp3:
        {
            applItem = FRM_PATTERN_SSP3;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp4:
        {
            applItem = FRM_PATTERN_SSP4;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp5:
        {
            applItem = FRM_PATTERN_SSP5;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp6:
        {
            applItem = FRM_PATTERN_SSP6;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp7:
        {
            applItem = FRM_PATTERN_SSP7;
            break;
        }
        case AL_RRC_SpecialSubframePatterns_ssp8:
        {
            applItem = FRM_PATTERN_SSP8;
            break;
        }
        default:
        {
            applItem = NUM_SUB_FRM_PATTERN;
            return false;
        }
    }
    return true;
}

